﻿using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Options;
using Shop.Core.Authorize;
using Shop.Core.Consts;
using Shop.Core.Helper;
using Shop.Core.Redis;
using Shop.Core.Repository;
using Shop.Core.Share;
using Shop.Core.WeChat;
using Shop.IServices;
using Shop.Model.Dto.Common;
using Shop.Model.Dto.User;
using Shop.Model.Entity.Use;
using Shop.Model.View;
using System.IdentityModel.Tokens.Jwt;
using System.Security.Claims;

namespace Shop.Services
{
    public class AccountAppService : IAccountAppService
    {
        private readonly JwtConfig _jwtConfig;
        private readonly WeChatService _weChatService;
        private readonly RepositoryBase<UseUser> _userRepository;
        private readonly IRedisProvider _redisProvider;
        private readonly IAliyunAppService _aliyunAppService;
        private readonly RepositoryBase<UserWallet> _walletRepository;

        public AccountAppService(RepositoryBase<UseUser> userRepository, 
            IOptions<JwtConfig> jwtConfig, 
            WeChatService weChatService,
            IRedisProvider redisProvider,
            IAliyunAppService aliyunAppService,
            RepositoryBase<UserWallet> walletRepository)
        {
            _userRepository = userRepository;
            _jwtConfig = jwtConfig.Value;
            _weChatService = weChatService;
            _redisProvider = redisProvider;
            _aliyunAppService = aliyunAppService;
            _walletRepository = walletRepository;
        }

        /// <summary>
        /// 小程序登录
        /// </summary>
        /// <param name="dto"></param>
        /// <returns></returns>
        /// <exception cref="NotImplementedException"></exception>
        public async Task<UseTokenInfoView> WeChatAppletLogin(WeChatLoginDto dto)
        {
            var sessionRepone = await _weChatService.GetJsCode2Session(dto.AppletCode);
            var oauthRepone = await _weChatService.GetOAuth2AccessToken(dto.GZHCode);

            var user = await _userRepository.GetFirstAsync(x => x.AppletOpenId == sessionRepone.OpenId && x.State != -1);
            if (user == null)
            {
                user = new UseUser
                {
                    Id = IdGenerater.GetNextId(),
                    UnionId = sessionRepone.OpenId,
                    AppletOpenId = sessionRepone.OpenId,
                    GongZongHaoOpenId = oauthRepone.OpenId,
                    SuperiorUser = dto.SuperiorId == 0 ? ShopConst.DefaultSuperior : dto.SuperiorId
                };

                if (user.SuperiorUser != ShopConst.DefaultSuperior)
                {
                    var exit = await _userRepository.AnyAsync(x => x.Id == user.SuperiorUser && x.State != -1);
                    if (!exit)
                        user.SuperiorUser = ShopConst.DefaultSuperior;
                }
                await _userRepository.InsertAsync(user);
                var userWallet = new UserWallet
                {
                    UserId = user.Id
                };
                await _walletRepository.InsertAsync(userWallet);
                var reslations = new List<UseUserRelation>
                {
                    new UseUserRelation
                    {
                        Id = IdGenerater.GetNextId(),
                        Ancestor = user.SuperiorUser,
                        Descendant = user.Id,
                        Level = 1,
                        CreateTime = DateTime.Now
                    }
                };
                if (user.SuperiorUser != ShopConst.DefaultSuperior)
                {
                    var rel = await _userRepository.Queryable<UseUserRelation>().Where(x => x.Descendant == user.SuperiorUser).ToListAsync();
                    foreach (var item in rel)
                    {
                        reslations.Add(new UseUserRelation()
                        {
                            Id = IdGenerater.GetNextId(),
                            Ancestor = item.Ancestor,
                            Descendant = user.Id,
                            Level = item.Level + 1,
                            CreateTime = DateTime.Now
                        });
                    }
                }
                await _userRepository.Change<UseUserRelation>().InsertRangeAsync(reslations);
            }

            if (user.State == -2)
                throw new Exception("账号状态异常");
            Claim[] claims = new Claim[] { new Claim(JwtRegisteredClaimNames.Jti, user.Id.ToString()), new Claim(ClaimTypes.DateOfBirth, DateTime.Now.ToString()) };
            return new UseTokenInfoView
            {
                Token = JwtToken.CreateToken(_jwtConfig, claims)
            };
        }

        /// <summary>
        /// 用户注册
        /// </summary>
        /// <param name="dto"></param>
        /// <param name="userId"></param>
        /// <returns></returns>
        /// <exception cref="Exception"></exception>
        public async Task<AppResult<UseTokenInfoView>> Register(UserRegistrationDto dto,long userId)
        {
            var user = await _userRepository.GetFirstAsync(x => x.Id == userId);
            if(user == null)
                return new AppResult<UseTokenInfoView>() { Code = 400,Errors=new string[] { "数据异常！" },Message="失败"};
            var data = await _redisProvider.StringGetAsync("dlc" + dto.Telephone);
            if (string.IsNullOrWhiteSpace(data) || data != dto.Code)
                return new ProblemDetails() { Detail = "验证码不正确或者已过期" };
            else
            {
                await _redisProvider.KeyDelAsync("dlc" + dto.Telephone);
                await _redisProvider.KeyDelAsync("dlr" + dto.Telephone);
            }

            if (!string.IsNullOrEmpty(user.Tele))
                return new AppResult<UseTokenInfoView>() { Code = 400, Errors = new string[] { "重复注册！" }, Message = "失败" };

            var isExit = await _userRepository.IsAnyAsync(x => x.Tele == dto.Telephone && x.State != -1);
            if(isExit)
                return new AppResult<UseTokenInfoView>() { Code = 400, Errors = new string[] { "电话号码已注册！" }, Message = "失败" };

            user.Tele = dto.Telephone;
            user.PassWord = dto.Password;

            _userRepository.Update(user);

            var useWallet = _walletRepository.Where(x => x.UserId == user.Id).First();
            if (useWallet == null)
                await _userRepository.Change<UserWallet>().InsertAsync(new UserWallet() { UserId = user.Id });

            return new UseTokenInfoView
            {
                Token = JwtToken.CreateToken(_jwtConfig, new Claim[] { new Claim(JwtRegisteredClaimNames.Jti, user.Id.ToString()), new Claim(ClaimTypes.DateOfBirth, DateTime.Now.ToString()) })
            };
        }

        /// <summary>
        /// 密码登录
        /// </summary>
        /// <param name="dto"></param>
        /// <returns></returns>
        public async Task<UseTokenInfoView> PasswordLogin(PasswordLoginDto dto)
        {
            var user = await _userRepository.GetFirstAsync(x => x.Tele == dto.Telphone && x.State != -1);
            if (user == null)
                throw new Exception("用户未注册！");
            if (string.IsNullOrEmpty(user.PassWord) || !user.PassWord.Equals(dto.Password))
                throw new Exception("密码错误");
            return new UseTokenInfoView
            {
                Token = JwtToken.CreateToken(_jwtConfig, new Claim[] { new Claim(JwtRegisteredClaimNames.Jti, user.Id.ToString()), new Claim(ClaimTypes.DateOfBirth, DateTime.Now.ToString()) })
            };
        }

        /// <summary>
        /// 发送登录验证码
        /// </summary>
        /// <param name="input"></param>
        /// <returns></returns>
        /// <exception cref="Exception"></exception>
        public async Task<AppResult> SendLoginCode(string input)
        {
            var isExit = await _userRepository.IsAnyAsync(x => x.Tele.Equals(input) && x.State != -1 && !string.IsNullOrEmpty(x.UnionId));
            if (isExit)
            {
                var data = await _redisProvider.StringGetAsync("dlr" + input);
                if (string.IsNullOrEmpty(data))
                {
                    Random rad = new();
                    int code = rad.Next(1000, 10000);
                    string smsCodeTemp = "{\"code\":\"" + code + "\"}";
                    _ = await _redisProvider.StringSetAsync("dlc" + input, code.ToString(), new TimeSpan(0, 5, 0));
                    _ = await _redisProvider.StringSetAsync("dlr" + input, "1", new TimeSpan(0, 1, 0));
                    return await _aliyunAppService.SendSms(new SendSmsDto
                    {
                        Tel = input,
                        Temp = "SMS_265650303",
                        TempParam = smsCodeTemp
                    });
                }
                else
                    return new AppResult(400, "失败","请不要频繁发送短信");
            }
            else
                return new AppResult(400, "失败", "用户不存在或者未激活");
        }

        /// <summary>
        /// 发送注册验证码
        /// </summary>
        /// <param name="input"></param>
        /// <returns></returns>
        /// <exception cref="Exception"></exception>
        public async Task<AppResult> SendRegisterCode(string input)
        {
            var isExit = await _userRepository.IsAnyAsync(x => x.Tele.Equals(input) && x.State != -1 && !string.IsNullOrEmpty(x.UnionId));
            if (isExit)
                return new AppResult(400, "失败", "电话号码已注册");
            else
            {
                var data = await _redisProvider.StringGetAsync("dlr" + input);
                if (string.IsNullOrEmpty(data))
                {
                    Random rad = new();
                    int code = rad.Next(1000, 10000);
                    string smsCodeTemp = "{\"code\":\"" + code + "\"}";
                    _ = await _redisProvider.StringSetAsync("dlc" + input, code.ToString(), new TimeSpan(0, 5, 0));
                    _ = await _redisProvider.StringSetAsync("dlr" + input, "1", new TimeSpan(0, 1, 0));
                    return await _aliyunAppService.SendSms(new SendSmsDto
                    {
                        Tel = input,
                        Temp = "SMS_265650303",
                        TempParam = smsCodeTemp
                    });
                }
                else
                    return new AppResult(400, "失败", "请不要频繁发送短信");
            }
        }

        /// <summary>
        /// 注册验证码验证
        /// </summary>
        /// <param name="input"></param>
        /// <returns></returns>
        /// <exception cref="Exception"></exception>
        public async Task<AppResult<bool>> CheckRegisterCode(PhoneCodeDto input)
        {
            var data = await _redisProvider.StringGetAsync("dlc" + input.Phone);
            if (string.IsNullOrWhiteSpace(data) || data != input.Code)
                return new AppResult<bool>(400, "失败", false, "验证码不正确或者已过期");
            else
            {
                await _redisProvider.KeyDelAsync("dlc" + input.Phone);
                await _redisProvider.KeyDelAsync("dlr" + input.Phone);
                return true;
            }
        }

        /// <summary>
        /// 验证码登录
        /// </summary>
        /// <param name="input"></param>
        /// <returns></returns>
        /// <exception cref="Exception"></exception>
        public async Task<AppResult<UseTokenInfoView>> SMSLogin(PhoneCodeDto input)
        {
            var data = await _redisProvider.StringGetAsync("dlc" + input.Phone);
            if (string.IsNullOrWhiteSpace(data) || data != input.Code)
                return new AppResult<UseTokenInfoView>(400, "验证码不正确或者已过期");

            await _redisProvider.KeyDelAsync("dlc" + input.Phone);
            await _redisProvider.KeyDelAsync("dlr" + input.Phone);
            var user = await _userRepository.GetFirstAsync(x => x.Tele == input.Phone && x.State != -1);
            if (user == null && string.IsNullOrEmpty(user.UnionId))
                return new AppResult<UseTokenInfoView>(400, "用户未注册");

            return new UseTokenInfoView
            {
                Token = JwtToken.CreateToken(_jwtConfig, new Claim[] { new Claim(JwtRegisteredClaimNames.Jti, user.Id.ToString()), new Claim(ClaimTypes.DateOfBirth, DateTime.Now.ToString()) })
            };
        }


        /// <summary>
        /// 根据电话号码获取token ,开发使用
        /// </summary>
        /// <param name="phone"></param>
        /// <returns></returns>
        /// <exception cref="Exception"></exception>
        public async Task<UseTokenInfoView> GetUserToken(string phone)
        {
            var user = await _userRepository.GetFirstAsync(x => x.Tele == phone);
            if (user == null)
                throw new Exception("用户不存在");
            return new UseTokenInfoView
            {
                Token = JwtToken.CreateToken(_jwtConfig, new Claim[] { new Claim(JwtRegisteredClaimNames.Jti, user.Id.ToString()) })
            };
        }
    }
}
